package com.open.anim;

import android.animation.TypeEvaluator;
import android.app.Activity;
import android.app.Application;
import android.app.FragmentManager;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.view.animation.Interpolator;

import com.open.anim.util.AnimUtils;
import com.open.anim.util.SystemUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @see <a href="https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#javadoctags">java doc参考文档</a><p>
 *
 * 1. 初始化 com.open.anim.OpenAnim.init(Context),传入相应的 Context, Activity, Fragmnet, View<p>
 * 2. 在 AndroidManifest.xml 加入配置类: (可选)<br>
 * <pre>
 * {@code
 * <meta-data<br>
 *      android:name="AnimModule"
 *      android:value="com.open.tvwidget.TestAnimModule" />
 * }
 * </pre>
 * 3. 使用的简单demo,具体参考DEMO代码或者 {@see com.open.anim.AnimatorManager} 的API函数.
 * <pre>
 * {@code
 * OpenAnim.into(button3)
 *         .duration(8000) // 总耗时
 *         .startDelay(888) // 开始延时
 * //      .interpolate(new BounceInterpolator()) // 插值器
 *         .repeatCount(3) // 重复次数
 * //      .repeatMode(ValueAnimator.RESTART) // 循环模式
 *         .together(new MoveXAnimator(), new AlphaAnimator(1.0f, 0.1f)) // 并行执行动画
 * //      .sequentially(new MoveAnimator(-200, 300), new AlphaAnimator(1.0f, 0.1f)) // 顺序执行动画
 * //      .play(new MoveAnimator())
 *         .start(); // 开始执行动画
 * }
 * </pre>
 * @author: hailong.qiu 356752238@qq.com
 */
// TODO 参考 Glide.with(context).resumeRequests(); Glide.with(context).pauseRequests();, 动画加入支持滚动
public class OpenAnim {

    static final String FRAGMENT_TAG = "com.open.anim";

    private static volatile OpenAnim openAnim;
    private static volatile boolean isInitializing;

    private Context mContext;
    private TypeEvaluator mTypeEvaluator;
    private Interpolator mInterpolator;
    private long mDuration = -1;
//    private final List<AnimatorManager> mAnimManagers = new ArrayList<>(); // 缓存 AnimtorManager
    private final Map<FragmentManager, AnimatorManagerFragment> pendingAnimManagerFragments =
            new HashMap<>(); // 缓存 AnimatorManagerFragment

    private static final String FRAGMENT_INDEX_KEY = "key";
    private final ArrayMap<View, android.app.Fragment> tempViewToFragment = new ArrayMap<View, android.app.Fragment>(); // 避免内存抖动
    private final Bundle tempBundle = new Bundle(); // 避免内存抖动

    OpenAnim(Context context, Interpolator interpolator, long duration, TypeEvaluator typeEvaluator) {
        Log.d(FRAGMENT_TAG, "OpenAnim create init");
        this.mContext = context.getApplicationContext();
        this.mInterpolator = interpolator;
        this.mDuration = duration;
        this.mTypeEvaluator = typeEvaluator;
    }

    /**
     * 当列表在滑动的时候，调用pauseAnimators()取消动画，滑动停止时，调用resumeAnimators()恢复动画。
     */
    public static void pauseAnimators() {

    }

    /**
     * 当列表在滑动的时候，调用pauseAnimators()取消动画，滑动停止时，调用resumeAnimators()恢复动画。
     */
    public static void resumeAnimators() {

    }

    /**
     * 想清除掉所有的动画的时候，这个方法可以帮助到你。
     */
    public static void clear() {

    }

    /**
     * 处理 Context 的生命周期回调
     *
     * @param context
     * @return {@see com.open.anim.AnimatorManager}
     */
    public static AnimatorManager with(Context context) {
        return getOpenAnim(context).get(context);
    }

    /**
     * 处理 v4 Fragment 的生命周期回调
     *
     * @param fragment
     * @return {@see com.open.anim.AnimatorManager}
     */
    public static AnimatorManager with(Fragment fragment) {
        return getOpenAnim(fragment.getActivity()).get(fragment.getActivity());
    }

    /**
     * 处理 android.app.Fragment 的生命周期回调
     *
     * @param fragment
     * @return {@see com.open.anim.AnimatorManager}
     */
    public static AnimatorManager with(android.app.Fragment fragment) {
        return getOpenAnim(fragment.getActivity()).get(fragment);
    }

    /**
     * 处理 view 的生命周期回调
     *
     * @param targeView 执行动画的view
     * @return {@see com.open.anim.AnimatorManager}
     */
    public static AnimatorManager with(View targeView) {
        return getOpenAnim(targeView.getContext()).get(targeView);
    }

    /**
     * 初始化，建议在前面调用，避免报错
     */
    private static OpenAnim getOpenAnim(Context context) {
        /* 线程安全单例模式 */
        if (openAnim == null) {
            synchronized (OpenAnim.class) {
                if (openAnim == null) {
                    /* 初始化全局配置文件 */
                    checkAndInitializeOpenAnim(context);
                }
            }
        }
        return openAnim;
    }

    public TypeEvaluator getTypeEvaluator() {
        return mTypeEvaluator;
    }

    /**
     * 获取动画的插值器
     *
     * @return Interpolator 插值器
     */
    public Interpolator getInterpolator() {
        return mInterpolator;
    }

    /**
     * 获取动画的总耗时
     *
     * @return long 总耗时
     */
    public long getDuration() {
        return mDuration;
    }

//    /**
//     * 生命周期回调添加
//     *
//     * @param animatorManager
//     */
//    void registerAnimatorManager(AnimatorManager animatorManager) {
//        synchronized (mAnimManagers) {
//            if (mAnimManagers.contains(animatorManager)) {
//                throw new IllegalStateException("Cannot register already registered manager");
//            }
//            mAnimManagers.add(animatorManager);
//        }
//    }
//
//    /**
//     * 生命周期监听取消回调
//     *
//     * @param {@see com.open.anim.AnimatorManager}
//     */
//    void unregisterAnimatorManager(AnimatorManager animatorManager) {
//        synchronized (mAnimManagers) {
//            if (!mAnimManagers.contains(animatorManager)) {
//                throw new IllegalStateException("Cannot unregister not yet registered manager");
//            }
//            mAnimManagers.remove(animatorManager);
//        }
//    }

    /**
     * @param context
     * @return {@see com.open.anim.AnimatorManager}
     */
    AnimatorManager get(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else if (SystemUtils.isOnMainThread() && !(context instanceof Application)) {
            if (context instanceof FragmentActivity) {
                return get((FragmentActivity) context);
                // } else if (context instanceof AppCompatActivity) {
                //     return get((AppCompatActivity)context);
            } else if (context instanceof Activity) {
                return get((Activity) context);
            } else if (context instanceof ContextWrapper) {
                return get(((ContextWrapper) context).getBaseContext());
            }
        }
        return getApplicationManager(context);
    }

    /**
     * 如果传入的是 View，需要去查找它所在的层级，然后进行.
     *
     * @param view
     * @return {@see com.open.anim.AnimatorManage}
     */
    AnimatorManager get(View view) {
        // Util.isOnBackgroundThread()
        // 查找 Activity.
        Activity activity = findActivity(view.getContext());

        if (activity == null) {
            return get(view.getContext().getApplicationContext());
        }

        if (activity instanceof FragmentActivity) {
            return get((FragmentActivity) activity);
        }

        // 查找 view 的 framgnet.
        android.app.Fragment fragment = findFragment(view, activity);

        if (fragment == null) {
            return get(activity);
        }

        return get(fragment);
    }

    /**
     * @param activity
     * @return {@see com.open.anim.AnimatorManager}
     */
    AnimatorManager get(Activity activity) {
        if (SystemUtils.isOnBackgroundThread()) {
            return get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            return fragmentGet(activity, activity.getFragmentManager(), /*parentHint=*/ null, isActivityVisible(activity));
        }
    }

    /**
     * @param fragment android.app.Fragment
     * @return {@see com.open.anim.AnimatorManager}
     */
    AnimatorManager get(android.app.Fragment fragment) {
        if (fragment.getActivity() == null) {
            throw new IllegalArgumentException(
                    "You cannot start a load on a fragment before it is attached");
        }
        //
        if (SystemUtils.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return get(fragment.getActivity().getApplicationContext());
        } else {
            return fragmentGet(fragment.getActivity(), fragment.getChildFragmentManager(), fragment, fragment.isVisible());
        }
    }

    /**
     * @param activity FragmentActivity
     * @return {@see com.open.anim.AnimatorManager}
     */
    AnimatorManager get(final FragmentActivity activity) {
        if (SystemUtils.isOnBackgroundThread()) {
            return get(activity.getApplicationContext());
        } else {
            OpenAnim oa = OpenAnim.getOpenAnim(activity);
            // return new AnimatorManager(oa, activity.getLifecycle());
            activity.getLifecycle().addObserver(new LifecycleObserver() {
                @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
                public void onStart() {
                    Log.d(TAG, "onStart");
                }
                @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
                public void onStop() {
                    Log.d(TAG, "onStop");
                }
                @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                public void onDestroy() {
                    activity.getLifecycle().removeObserver(this);
                    Log.d(TAG, "onDestroy");
                }
            });
            return mAnimManagerFactory.build(oa, activity.getLifecycle());
        }
    }

    /**
     * 代码参考 Glide 代码
     * 查找 targetView 的 Fragment
     *
     * @param target
     * @param activity
     * @return
     */
    private android.app.Fragment findFragment(
            @NonNull View target,
            @NonNull Activity activity) {
        tempViewToFragment.clear();
        findAllFragmentsWithViews(activity.getFragmentManager(), tempViewToFragment);
        android.app.Fragment result = null;
        View activityRoot = activity.findViewById(android.R.id.content);
        View current = target;
        // 直到为 content 布局 就退出 while.
        while (!activityRoot.equals(current)) {
            result = tempViewToFragment.get(current);
            if (result != null) {
                break;
            }
            if (current.getParent() instanceof View) {
                current = (View) current.getParent();
            } else {
                break;
            }
        }
        tempViewToFragment.clear();
        return result;
    }

    /**
     * @param fragmentManager
     * @param result
     */
    private void findAllFragmentsWithViews(
            @NonNull android.app.FragmentManager fragmentManager,
            @NonNull ArrayMap<View, android.app.Fragment> result) {
        // >= VERSION_CODES.O 26
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            for (android.app.Fragment fragment : fragmentManager.getFragments()) {
                if (fragment.getView() != null) {
                    result.put(fragment.getView(), fragment);
                    findAllFragmentsWithViews(fragment.getChildFragmentManager(), result);
                }
            }
        } else {
            findAllFragmentsWithViewsPreO(fragmentManager, result);
        }
    }

    /**
     * @param fragmentManager
     * @param result void
     */
    private void findAllFragmentsWithViewsPreO(
            @NonNull android.app.FragmentManager fragmentManager,
            @NonNull ArrayMap<View, android.app.Fragment> result) {
        int index = 0;
        while (true) {
            tempBundle.putInt(FRAGMENT_INDEX_KEY, index);
            android.app.Fragment fragment = null;
            try {
                fragment = fragmentManager.getFragment(tempBundle, FRAGMENT_INDEX_KEY);
            } catch (Exception e) {
            }

            if (fragment == null) {
                break;
            }

            if (fragment.getView() != null) {
                result.put(fragment.getView(), fragment);
                // >= JELLY_BEAN_MR1 4.2 17
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                    findAllFragmentsWithViews(fragment.getChildFragmentManager(), result);
                }
            }
        }
    }

    static final String TAG = "com.open.tvwidget";

    final Map<FragmentManager, AnimatorManagerFragment> mCacheAnimManagerFragmnet =
            new HashMap<>();

    /**
     * @param context
     * @param fm
     * @param parentHint
     * @param isParentVisible
     * @return {@see com.open.anim.AnimatorManager}
     */
    AnimatorManager fragmentGet(
            @NonNull Context context,
            @NonNull final android.app.FragmentManager fm,
            @Nullable android.app.Fragment parentHint,
            boolean isParentVisible) {
        final AnimatorManagerFragment current = getAnimatorManagerFragment(fm, parentHint, isParentVisible);
        OpenAnim oa = OpenAnim.getOpenAnim(context);
        current.getLifecycle().addObserver(new LifecycleObserver() {
            @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
            public void onStart() {
                Log.d(TAG, "onStart");
            }
            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            public void onStop() {
                Log.d(TAG, "onStop");
            }
            @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
            public void onDestroy() {
                mCacheAnimManagerFragmnet.remove(fm);
                current.getLifecycle().removeObserver(this);
                Log.d(TAG, "onDestroy-" + mCacheAnimManagerFragmnet.size());
            }
        });
        return mAnimManagerFactory.build(oa, current.getLifecycle());
    }

    private AnimatorManagerFragment getAnimatorManagerFragment(
            @NonNull final android.app.FragmentManager fm,
            @Nullable android.app.Fragment parentHint,
            boolean isParentVisible) {
        AnimatorManagerFragment current = (AnimatorManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (null == current) {
            current = mCacheAnimManagerFragmnet.get(fm);
            if (null == current) {
                current = new AnimatorManagerFragment();
                fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
                mCacheAnimManagerFragmnet.put(fm, current);
            }
        }
        return current;
    }

    /**
     * 如果找不到相关的 Activity, Fragmnet,
     * 那就给一个默认的,没有周期监听的.
     *
     * @param context
     * @return {@see com.open.anim.AnimatorManager}
     */
    AnimatorManager getApplicationManager(@NonNull Context context) {
        OpenAnim oa = OpenAnim.getOpenAnim(context);
        return mAnimManagerFactory.build(oa, null);
        // return new AnimatorManager(oa, null);
    }

    private static void assertNotDestroyed(@NonNull Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
            throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
        }
    }

    /**
     * 判断 activity 是否已经 Finish
     *
     * @param activity
     * @return
     */
    private static boolean isActivityVisible(Activity activity) {
        // This is a poor heuristic, but it's about all we have. We'd rather err on the side of visible
        // and start requests than on the side of invisible and ignore valid requests.
        return !activity.isFinishing();
    }

    private Activity findActivity(@NonNull Context context) {
        if (context instanceof Activity) {
            return (Activity) context;
        } else if (context instanceof ContextWrapper) {
            return findActivity(((ContextWrapper) context).getBaseContext());
        } else {
            return null;
        }
    }

    private static void checkAndInitializeOpenAnim(@NonNull Context context) {
        if (isInitializing) {
            throw new IllegalStateException("正在初始化OpenAnim");
        }
        isInitializing = true;
        /* 初始化全局配置文件 */
        initializeOpenAnim(context);
        isInitializing = false;
    }

    private static void initializeOpenAnim(@NonNull Context context) {
        /* 初始化全局配置文件 */
        AnimModule animModule = AnimUtils.getMetaData(context);
        /* 如果没有，则设置默认配置类 */
        if (null == animModule) {
            animModule = new BaseAnimModule();
        }
        /* 获取配置属性 */
        AnimConfigBuilder builder = new AnimConfigBuilder();
        animModule.applyOptions(context, builder);
        /* OpenAnim init */
        OpenAnim.openAnim = builder.build(context);
    }

    public interface AnimatorManagerFactory {
        AnimatorManager build(
                OpenAnim openAnim,
                Lifecycle lifecycle);
    }

    final AnimatorManagerFactory mAnimManagerFactory = new AnimatorManagerFactory() {
        @Override
        public AnimatorManager build(OpenAnim openAnim, Lifecycle lifecycle) {
            return new AnimatorManager(openAnim, lifecycle);
        }
    };

    public Context getContext() {
        return mContext;
    }

    @Override
    public String toString() {
        return "OpenAnim{" +
                "mInterpolator=" + mInterpolator +
                ", mDuration=" + mDuration +
                '}';
    }
}
